﻿using Apache.IoTDB;
using Apache.IoTDB.DataStructure;
using Mohe.Scada.Config;
using Mohe.Scada.Util.Extensions;
using Mohe.Scada.Util.Models;
using Mohe.Scada.Util.TimeSeries;
using NLog;
using System.Text;

namespace Mohe.Scada.Common.TimeSeries
{
    public class IotDbStorage : IStorage
    {
        private static Logger _logger = LogManager.GetCurrentClassLogger();
        private SessionPool _session;
        private static string _TelemetryDataDB = $"root.device{LocalSetting.Tenant}";
        private static string _MunkReportDataDB = $"root.munk{LocalSetting.Tenant}";
        private static string _TemperatureDataDB = $"root.temperature{LocalSetting.Tenant}";

        public IotDbStorage()
        {
            List<string> nodeList = LocalSetting.TsSetting.Address.Split(";").ToList();
            _session = new SessionPool.Builder()
                .SetNodeUrl(nodeList)
                .SetUsername(LocalSetting.TsSetting.UserName)
                .SetPassword(LocalSetting.TsSetting.Password)
                .SetFetchSize(1024)
                .Build();
        }
        public async Task<List<MunkReportData>> LoadMunkReportData(int StationNo, int SerialNum)
        {
            try
            {

                var strSql = new StringBuilder($"select * from {_MunkReportDataDB} where 1=1");
                if (SerialNum > 0)
                {
                    strSql.Append($" and SerialNum = {SerialNum}");
                }
                if (StationNo > 0)
                {
                    strSql.Append($" and StationNo = {StationNo}");
                }
                var resp = await _session.ExecuteQueryStatementAsync(strSql.ToString());
                var lst = new List<MunkReportData>();
                while (resp.HasNext())
                {
                    var entity = new MunkReportData();
                    var rowRecord = resp.Next();
                    for (int i = 0; i < rowRecord.Measurements.Count; i++)
                    {

                        switch (rowRecord.Measurements[i])
                        {
                            case "StationNo":
                                entity.StationNo = rowRecord.Values[i].ToInt();
                                break;
                            case "SerialNum":
                                entity.SerialNum = rowRecord.Values[i].ToInt();
                                break;
                            case "CurActual":
                                entity.CurActual = rowRecord.Values[i].ToFloat();
                                break;
                            case "VolActual":
                                entity.VolActual = rowRecord.Values[i].ToFloat();
                                break;
                            case "SetCurr":
                                entity.SetCurr = rowRecord.Values[i].ToFloat();
                                break;
                            case "SetVol":
                                entity.SetVol = rowRecord.Values[i].ToFloat();
                                break;
                            case "ReportDate":
                                entity.ReportDate = (DateTime)rowRecord.Values[i];
                                break;
                        }
                    }
                    lst.Add(entity);
                }
                return lst;
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
            return null;
        }

        public async Task<List<TelemetryData>> LoadTelemetryAsync(string keys, DateTime begin, DateTime end, TimeSpan every, EnumAggregate aggregate)
        {
            var kvs = keys.Split(';', ',');
            var strSql = new StringBuilder("select ");
            foreach (var key in kvs)
            {
                if (aggregate != EnumAggregate.None)
                {
                    strSql.Append(aggregate.ToString() + "(");
                }
                strSql.Append($"\"{key}\"");
                if (aggregate != EnumAggregate.None)
                {
                    strSql.Append(")");
                }
                strSql.Append(",");
            }
            strSql.Remove(strSql.Length - 1, 1);
            strSql.Append($" from {_TelemetryDataDB} where  time>={begin} and time <= '{end}' ");
            if (every != TimeSpan.Zero)
            {
                strSql.Append($" group by date_bin({every.TotalMilliseconds}ms,time)  FILL METHOD PREVIOUS");
            }

            _logger.Debug(strSql.ToString());
            var resp = await _session.ExecuteQueryStatementAsync(strSql.ToString());
            var lst = new List<TelemetryData>();
            while (resp.HasNext())
            {

                var rowRecord = resp.Next();
                for (int i = 0; i < rowRecord.Measurements.Count; i++)
                {
                    lst.Add(new TelemetryData { Timestamp = rowRecord.GetDateTime(), Value = rowRecord.Values[i], VarName = rowRecord.Measurements[i] });
                }
            }
            return lst;
        }


        private async Task<List<TemperatureData>> QueryTemperatureAsync(string sql)
        {
            try
            {
                var resp = await _session.ExecuteQueryStatementAsync(sql);
                var lst = new List<TemperatureData>();
                while (resp.HasNext())
                {
                    var entity = new TemperatureData();
                    var rowRecord = resp.Next();
                    for (int i = 0; i < rowRecord.Measurements.Count; i++)
                    {

                        switch (rowRecord.Measurements[i])
                        {
                            case "StationNo":
                                entity.StationNo = rowRecord.Values[i].ToInt();
                                break;
                            case "SerialNum":
                                entity.SerialNum = rowRecord.Values[i].ToInt();
                                break;
                            case "SetTemp":
                                entity.SetTemp = rowRecord.Values[i].ToFloat();
                                break;
                            case "TempActual":
                                entity.TempActual = rowRecord.Values[i].ToFloat();
                                break;
                            case "ReportDate":
                                entity.ReportDate = (DateTime)rowRecord.Values[i];
                                break;
                        }
                    }
                    lst.Add(entity);
                }
                return lst;
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
            return null;
        }
        public async Task<List<TemperatureData>> LoadTemperatureAsync(int StationNo, int SerialNum = 0, int pageSize = 1000, int pageIndex = 1)
        {
            StringBuilder strSql = new StringBuilder();
            strSql.Append($"select * from {_TemperatureDataDB} where 1=1 ");
            if (StationNo > 0)
            {
                strSql.Append($" and StationNo={StationNo}");
            }
            if (SerialNum > 0)
            {
                strSql.Append($" and SerialNum={SerialNum}");
            }

            strSql.Append($" OFFSET {(pageIndex - 1) * pageSize} LIMIT {pageSize}");
            return await QueryTemperatureAsync(strSql.ToString());
        }

        public async Task<List<TemperatureData>> LoadTemperatureAsync(string beginTime, string endTime, int StationNo, int pageSize = 1000, int pageIndex = 1)
        {
            StringBuilder strSql = new StringBuilder();
            strSql.Append($"select * from {_TemperatureDataDB} where 1=1 ");
            if (StationNo > 0)
            {
                strSql.Append($" and StationNo={StationNo}");
            }
            if (!string.IsNullOrEmpty(beginTime))
            {
                strSql.Append($" and ReportDate>={beginTime.ToDateTime()}");
            }
            if (!string.IsNullOrEmpty(endTime))
            {
                strSql.Append($" and ReportDate>={endTime.ToDateTime()}");
            }
            strSql.Append($" OFFSET {(pageIndex - 1) * pageSize} LIMIT {pageSize}");
            return await QueryTemperatureAsync(strSql.ToString());
        }

        public async Task<bool> StoreMunkReportAsync(List<MunkReportData> data)
        {
            try
            {
                List<RowRecord> rowRecords = new List<RowRecord>();
                List<string> devices = new List<string>();
                foreach (var item in data)
                {
                    var measures = new List<string> { "ReportDate", "SetCurr", "CurActual", "SetVol", "VolActual", "MunkId", "SerialNum" };
                    var values = new List<object> { item.ReportDate, item.SetCurr, item.CurActual, item.SetVol, item.VolActual, item.MunkId, item.SerialNum };
                    var types = new List<TSDataType> { TSDataType.TIMESTAMP, TSDataType.FLOAT, TSDataType.FLOAT, TSDataType.FLOAT, TSDataType.FLOAT, TSDataType.INT32, TSDataType.INT32 };
                    devices.Add(_MunkReportDataDB);
                    rowRecords.Add(new RowRecord(DateTime.Now, values, measures, types));
                }
                await _session.InsertRecordsAsync(devices, rowRecords);

            }
            catch (Exception ex)
            {

                _logger.Error(ex, " Store MunkReport Error.");
                return false;
            }
            return true;
        }

        public async Task<bool> StoreTelemetryAsync(PayLoad telemetryData)
        {
            try
            {
                var measures = new List<string>();
                var values = new List<object>();
                var types = new List<TSDataType>();
                foreach (var key in telemetryData.Values.Keys)
                {
                    measures.Add(key);
                    values.Add(telemetryData.Values[key].ToString());
                    types.Add(TSDataType.STRING);
                }
                var rowRecord = new RowRecord(telemetryData.LastUpdateTime, values, measures, types);
                await _session.InsertRecordAsync(_TelemetryDataDB, rowRecord);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, " Store Telemetry Error.");
                return false;
            }
            return true;
        }

        public async Task<bool> StoreTemperatureAsync(List<TemperatureData> data)
        {
            try
            {
                List<RowRecord> rowRecords = new List<RowRecord>();
                List<string> devices = new List<string>();
                foreach (var item in data)
                {
                    var measures = new List<string> { "ReportDate", "SetTemp", "TempActual", "StationNo", "SerialNum" };
                    var values = new List<object> { item.ReportDate, item.SetTemp, item.TempActual, item.StationNo, item.SerialNum };
                    var types = new List<TSDataType> { TSDataType.TIMESTAMP, TSDataType.FLOAT, TSDataType.FLOAT, TSDataType.INT32, TSDataType.INT32 };
                    devices.Add(_TemperatureDataDB);
                    rowRecords.Add(new RowRecord(DateTime.Now, values, measures, types));
                }
                await _session.InsertRecordsAsync(devices, rowRecords);

            }
            catch (Exception ex)
            {
                _logger.Error(ex, " Store MunkReport Error.");
                return false;
            }
            return true;
        }
    }
}
